/**
 * Title :	Property view
 * Description: Property editor
 * Version:	provider
 * Copyright (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 */
package bsys.propertyview;

import java.awt.Component;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.io.File;
import java.net.URISyntaxException;
import java.util.EventObject;
import java.util.Set;

import javax.swing.CellEditor;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.TableCellEditor;

import bsys.propertyview.PropertyView.Attribute;
import bsys.propertyview.PropertyView.Handler;

/**
 * Versatile super editor
 * 
 * @author Bernard Bou
 */
public class Editor implements TableCellEditor, CellEditorListener
{
	private static final long serialVersionUID = 1L;

	/**
	 * View
	 */
	private final PropertyView thePropertyView;

	/**
	 * Handler used to retrieve id map
	 */
	private Handler theHandler;

	/**
	 * Attribute being edited
	 */
	private Attribute theAttribute;

	/**
	 * Color editor
	 */
	private ColorEditor theColorEditor;

	/**
	 * File editor
	 */
	private FileEditor theFileEditor;

	/**
	 * List editor
	 */
	private ListEditor theListEditor;

	/**
	 * Long text editor
	 */
	private LongTextEditor theLongTextEditor;

	/**
	 * Default editor
	 */
	private DefaultCellEditor theDefaultEditor;

	/**
	 * The delegate subeditor
	 */
	private TableCellEditor theSubEditor;

	/**
	 * The subeditors
	 */
	private TableCellEditor[] theSubEditors;

	/**
	 * Constructor
	 * 
	 * @param thisPropertyView
	 *        view
	 */
	public Editor(PropertyView thisPropertyView)
	{
		thePropertyView = thisPropertyView;

		// subeditors
		theColorEditor = new ColorEditor();
		theFileEditor = new FileEditor();
		theListEditor = new ListEditor();
		theLongTextEditor = new LongTextEditor();
		theDefaultEditor = new DefaultCellEditor(new JTextField());
		theSubEditors = new TableCellEditor[] { theColorEditor, theFileEditor, theListEditor, theLongTextEditor, theDefaultEditor };

		// edited attribute
		theAttribute = null;
	}

	/**
	 * Set get/set handlers
	 * 
	 * @param thisHandler
	 *        handler
	 */
	public void setHandler(Handler thisHandler)
	{
		theHandler = thisHandler;
	}

	// I N T E R F A C E

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.table.TableCellEditor#getTableCellEditorComponent(javax.swing.JTable, java.lang.Object, boolean, int, int)
	 */
	public Component getTableCellEditorComponent(JTable table, Object thisValue, boolean isSelected, int row, int column)
	{
		if (column == PropertyView.VALUE)
		{
			theAttribute = (Attribute) thisValue;

			// pick subeditor
			switch (theAttribute.theDescriptor.theType)
			{
			// color
			case COLOR:
				theSubEditor = theColorEditor;
				break;

			// lists
			case STROKE:
				theListEditor.setEditable(false);
				theListEditor.setImageMap(PropertyView.theStrokeIcons);
				populateWithStrokes(theListEditor.getComboBox());
				theSubEditor = theListEditor;
				break;

			case TERMINATOR:
				theListEditor.setImageMap(PropertyView.theTerminatorIcons);
				theListEditor.setEditable(false);
				populateWithTerminators(theListEditor.getComboBox());
				theSubEditor = theListEditor;
				break;

			case FONTFACE:
				theListEditor.setImageMap(null);
				theListEditor.setEditable(false);
				populateWithFonts(theListEditor.getComboBox());
				theSubEditor = theListEditor;
				break;

			case BOOLEAN:
				theListEditor.setImageMap(null);
				theListEditor.setEditable(false);
				populateWithTrueFalse(theListEditor.getComboBox());
				theSubEditor = theListEditor;
				break;

			case REFID:
				theListEditor.setImageMap(null);
				theListEditor.setEditable(false);
				populateWithIds(theListEditor.getComboBox());
				theSubEditor = theListEditor;
				break;

			case LINK:
				theListEditor.setImageMap(null);
				theListEditor.setEditable(true);
				populateWithUrls(theListEditor.getComboBox());
				addPopulateWithSharpIds(theListEditor.getComboBox());
				theSubEditor = theListEditor;
				break;

			// files
			case IMAGE:
				theFileEditor.setDirectory(getImageRepository());
				theSubEditor = theFileEditor;
				break;

			// text
			case LONGTEXT:
				theSubEditor = theLongTextEditor;
				break;

			case TEXT:
				if (theAttribute.theDescriptor.thePossibleValues != null)
				{
					theListEditor.setImageMap(null);
					theListEditor.setEditable(false);
					populateWithStrings(theListEditor.getComboBox(), theAttribute.theDescriptor.thePossibleValues);
					theSubEditor = theListEditor;
					break;
				}
			// fall through

			// text
			case INTEGER:
			case DOUBLE:
			case ID:
			case FONTSIZE:
			default:
				theSubEditor = theDefaultEditor;
				break;
			}

			// invoke subeditor
			return theSubEditor.getTableCellEditorComponent(table, theAttribute.theValue, isSelected, row, column);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.CellEditor#getCellEditorValue()
	 */
	public Object getCellEditorValue()
	{
		switch (theAttribute.theDescriptor.theType)
		{
		case BOOLEAN:
		{
			String thisValue = (String) theSubEditor.getCellEditorValue();
			if (thisValue == null || thisValue.equals(PropertyView.theDefaultString))
				return null;
			return Boolean.valueOf(thisValue);
		}

		case INTEGER:
		case FONTSIZE:
		{
			String thisValue = (String) theSubEditor.getCellEditorValue();
			if (thisValue == null || thisValue.equals(PropertyView.theDefaultString))
				return null;
			try
			{
				return Integer.valueOf(thisValue);
			}
			catch (NumberFormatException e)
			{
			}
			return null;
		}

		case DOUBLE:
		{
			String thisValue = (String) theSubEditor.getCellEditorValue();
			if (thisValue == null || thisValue.equals(PropertyView.theDefaultString))
				return null;
			try
			{
				return Double.valueOf(thisValue);
			}
			catch (NumberFormatException e)
			{
			}
			return null;
		}

		case COLOR:
		{
			return theSubEditor.getCellEditorValue();
		}

		default:
		{
			String thisValue = (String) theSubEditor.getCellEditorValue();
			if (thisValue == null || thisValue.isEmpty() || thisValue.equals(PropertyView.theDefaultString))
				return null;
			return thisValue;
		}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.CellEditor#shouldSelectCell(java.util.EventObject)
	 */
	public boolean shouldSelectCell(EventObject thisEvent)
	{
		return theSubEditor.shouldSelectCell(thisEvent);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.CellEditor#stopCellEditing()
	 */
	public boolean stopCellEditing()
	{
		return theSubEditor.stopCellEditing();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.DefaultCellEditor#cancelCellEditing()
	 */
	public void cancelCellEditing()
	{
		theSubEditor.cancelCellEditing();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.CellEditor#isCellEditable(java.util.EventObject)
	 */
	public boolean isCellEditable(EventObject thisAnEvent)
	{
		return true;
	}

	// H E L P E R S

	/**
	 * Get image repository
	 * 
	 * @return image repository as file
	 */
	private File getImageRepository()
	{
		try
		{
			return new File(thePropertyView.theImageRepository.toURI());
		}
		catch (URISyntaxException e)
		{
		}
		return null;
	}

	// P O P U L A T E . C O M B O B O X

	/**
	 * Populate combobox with ids
	 * 
	 * @param thisCombo
	 *        combobox
	 */
	private void populateWithIds(JComboBox thisCombo)
	{
		Set<String> theseIds = theHandler.theIdGetter.ids();
		thisCombo.removeAllItems();
		thisCombo.addItem(PropertyView.theDefaultString);
		for (String thisString : theseIds)
			thisCombo.addItem(thisString);
	}

	/**
	 * Populate combobox with stroke types
	 * 
	 * @param thisCombo
	 *        combobox
	 */
	private void populateWithStrokes(JComboBox thisCombo)
	{
		populateWithStrings(thisCombo, PropertyView.theStrokeStrings);
	}

	/**
	 * Populate combobox with true/false
	 * 
	 * @param thisCombo
	 *        combobox
	 */
	private void populateWithTrueFalse(JComboBox thisCombo)
	{
		populateWithStrings(thisCombo, new String[] { "false", "true" }); //$NON-NLS-1$ //$NON-NLS-2$
	}

	/**
	 * Populate combobox with urls
	 * 
	 * @param thisCombo
	 *        combobox
	 */
	private void populateWithUrls(JComboBox thisCombo)
	{
		populateWithStrings(thisCombo, new String[] { "http://", "ftp://" }); //$NON-NLS-1$ //$NON-NLS-2$
	}

	/**
	 * Populate combobox with ids referencing nodes
	 * 
	 * @param thisCombo
	 *        combobox
	 */
	private void addPopulateWithSharpIds(JComboBox thisCombo)
	{
		Set<String> theseIds = theHandler.theIdGetter.ids();
		for (String thisString : theseIds)
			thisCombo.addItem("#" + thisString); //$NON-NLS-1$
	}

	/**
	 * Populate combobox with fonts
	 * 
	 * @param thisCombo
	 *        combobox
	 */
	private void populateWithFonts(JComboBox thisCombo)
	{
		populateWithStrings(thisCombo, new String[] { "SansSerif", "Serif", "MonoSpaced", "Dialog", "DialogInput" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
		Font[] theseFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
		for (int i = 0; i < theseFonts.length; i++)
			thisCombo.addItem(theseFonts[i].getFontName());
	}

	/**
	 * Populate combobox with terminators
	 * 
	 * @param thisCombo
	 *        combobox
	 */
	private void populateWithTerminators(JComboBox thisCombo)
	{
		populateWithStrings(thisCombo, PropertyView.theTerminatorStrings);
	}

	/**
	 * Populate combobox with strings list
	 * 
	 * @param thisCombo
	 *        combobox
	 * @param theseStrings
	 *        list of strings
	 */
	private void populateWithStrings(JComboBox thisCombo, String[] theseStrings)
	{
		thisCombo.removeAllItems();
		thisCombo.setMaximumRowCount(theseStrings.length + 1);
		thisCombo.removeAllItems();
		thisCombo.addItem(PropertyView.theDefaultString);
		for (String thisString : theseStrings)
			thisCombo.addItem(thisString);
	}

	// N O T I F I C A T I O N

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.event.CellEditorListener#editingCanceled(javax.swing.event.ChangeEvent)
	 */
	public void editingCanceled(ChangeEvent e)
	{
		System.out.println("Editor notification: Canceled"); //$NON-NLS-1$
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.event.CellEditorListener#editingStopped(javax.swing.event.ChangeEvent)
	 */
	public void editingStopped(ChangeEvent e)
	{
		System.out.println("Editor notification: Stopped"); //$NON-NLS-1$
	}

	// L I S T E N E R S

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.CellEditor#addCellEditorListener(javax.swing.event.CellEditorListener)
	 */
	public void addCellEditorListener(CellEditorListener thisListener)
	{
		for (CellEditor thisSubEditor : theSubEditors)
			thisSubEditor.addCellEditorListener(thisListener);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.CellEditor#removeCellEditorListener(javax.swing.event.CellEditorListener)
	 */
	public void removeCellEditorListener(CellEditorListener thisListener)
	{
		for (CellEditor thisSubEditor : theSubEditors)
			thisSubEditor.removeCellEditorListener(thisListener);
	}
}